/*
 *
 * Copyright 2017 Asylo authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

#ifndef ASYLO_IDENTITY_SEALING_SGX_SGX_LOCAL_SECRET_SEALER_H_
#define ASYLO_IDENTITY_SEALING_SGX_SGX_LOCAL_SECRET_SEALER_H_

#include <memory>

#include "asylo/crypto/util/byte_container_view.h"
#include "asylo/identity/identity.pb.h"
#include "asylo/identity/platform/sgx/code_identity.pb.h"
#include "asylo/identity/platform/sgx/sgx_identity.pb.h"
#include "asylo/identity/sealing/sealed_secret.pb.h"
#include "asylo/identity/sealing/secret_sealer.h"
#include "asylo/util/cleansing_types.h"
#include "asylo/util/status.h"

namespace asylo {

/// An implementation of the SecretSealer abstract interface that binds the
/// secrets to the enclave identity on a local machine. The secrets sealed by
/// this sealer can only be unsealed on the same machine.
///
/// The SgxLocalSecretSealer can be configured to seal secrets either to
/// MRENCLAVE or MRSIGNER. The SgxLocalSecretSealer class provides two factory
/// methods--one that creates an SgxLocalSecretSealer configured to seal secrets
/// to MRENCLAVE and another that creates an SgxLocalSecretSealer configured to
/// seal secrets to MRSIGNER. In the MRENCLAVE-sealing mode, the default
/// SealedSecretHeader generated by the sealer binds the secrets to the
/// MRENCLAVE portion of the enclave's identity. In the MRSIGNER mode, the
/// default SealedSecretHeader generated by the sealer binds the secrets to the
/// MRSIGNER portion of the enclave's identity. In either mode, the sealed
/// secret is bound to all bits of MISCSELECT, and all security-sensitive bits
/// of ATTRIBUTES, as defined in secs_attributes.h.
///
/// Note that the SealedSecretHeader provided to the Seal() method also controls
/// the cipher suite used for sealing the secret. The default setting of this
/// field (as populated by SetDefaultHeader() ) is subject to change.  As a
/// result, users must not make any assumptions about the default cipher suite.
/// If they wish to use a specific cipher suite, they must manually verify or
/// override the cipher suite set by the SetDefaultHeader() method.
///
/// Sample usage for Seal():
/// ```
///   std::unique_ptr<SgxLocalSecretSealer> sealer = CreateMrsignerSealer();
///
///   SealedSecretHeader header;
///   // Fill out the portions of the header that must be set by the client.
///   header.set_secret_name("my name");
///   header.set_secret_version("my version");
///   header.set_secret_purpose("my purpose");
///   // secret_handling_policy is a client-specific string, which, for example,
///   could be a serialized proto.
///   string secret_handling_policy = ...
///   header.set_secret_handling_policy(secret_handling_policy);
///   sealer->SetDefaultHeader(&header);
///
///   // Override fields in the default header, if desired.
///   ...
///
///   // Generate the secret to be sealed.
///   std::vector<uint8_t, CleansingAllocator> secret = ...;
///
///   // Generate the additional authenticated data to be tied to the secret.
///   string additional_authenticated_data;
///   addtional_authenticated_data = ...
///
///   // Seal the secret and the additional authenticated data.
///   SealedSecret sealed_secret;
///   Status status = sealer->Seal(header, additional_authenticated_data,
///                                secret, &sealed_secret);
/// ```
///
/// Sample usage for Unseal():
/// ```
///   std::unique_ptr<SgxLocalSecretSealer> sealer = CreateMrsignerSealer();
///   SealedSecret sealed_secret;
///   ...
///   sealed_secret.ParseFromString(...);
///
///   std::vector<uint8_t, CleansingAllocator<uint8_t>> secret;
///   ASYLO_RETURN_IF_ERROR(sealer->Unseal(sealed_secret, secret));
///   // secret now holds the unsealed secret. The policy and
///   // additional_authenticated_data in the sealed_secret are now
///   // authenticated.
/// ```
///
/// It should be noted that the SgxLocalSecretSealer's configuration only
/// affects the default header generated by the sealer. Users can override the
/// generated default header. A sealer in either MRENCLAVE or MRSIGNER
/// configuration can unseal secrets that are sealed by a sealer in either
/// configuration.
class SgxLocalSecretSealer : public SecretSealer {
 public:
  /// Creates an SgxLocalSecretSealer that seals secrets to the MRENCLAVE part
  /// of the enclave code identity.
  ///
  /// \return A smart pointer that owns the created sealer.
  static std::unique_ptr<SgxLocalSecretSealer> CreateMrenclaveSecretSealer();

  /// Creates an SgxLocalSecretSealer that seals secrets to the MRSIGNER part of
  /// the enclave identity.
  ///
  /// \return A smart pointer that owns the created sealer.
  static std::unique_ptr<SgxLocalSecretSealer> CreateMrsignerSecretSealer();

  SgxLocalSecretSealer(const SgxLocalSecretSealer &other) = delete;
  virtual ~SgxLocalSecretSealer() = default;

  SgxLocalSecretSealer &operator=(const SgxLocalSecretSealer &other) = delete;

  // From SecretSealer interface.
  SealingRootType RootType() const override;
  std::string RootName() const override;
  std::vector<EnclaveIdentityExpectation> RootAcl() const override;
  Status SetDefaultHeader(SealedSecretHeader *header) const override;
  StatusOr<size_t> MaxMessageSize(
      const SealedSecretHeader &header) const override;
  StatusOr<uint64_t> MaxSealedMessages(
      const SealedSecretHeader &header) const override;
  Status Seal(const SealedSecretHeader &header,
              ByteContainerView additional_authenticated_data,
              ByteContainerView secret, SealedSecret *sealed_secret) override;
  Status Unseal(const SealedSecret &sealed_secret,
                CleansingVector<uint8_t> *secret) override;

 private:
  // Instantiates LocalSecretSealer that sets client_acl in the default sealed
  // secret header per |default_client_acl|.
  SgxLocalSecretSealer(const SgxIdentityExpectation &default_client_acl);

  // The default client ACL for this SecretSealer.
  SgxIdentityExpectation default_client_acl_;
};

}  // namespace asylo

#endif  // ASYLO_IDENTITY_SEALING_SGX_SGX_LOCAL_SECRET_SEALER_H_
